Mark Rejhon's 3:2 Pulldown Algorithm
Here's a simplified pseudocode implementation of my 3:2 pulldown detection algorithm. You might even
easily be able to implement this into DScaler. Almost all of us
really don't care about 2:2 pulldown at all as I only see this
happening only 0.1 to 0.5% of the time on NTSC television.
Without further ado, here you go!
START PSEUDOCODE FOR AUTOMATIC 3:2 PULLDOWN
DETECTION
set MAX_MISMATCH = 12
set MAX_VERIFY_CYCLES = 2
set MISMATCH_COUNT = 0
set MOVIE_FIELD_CYCLE = 0
set MOVIE_VERIFY_CYCLE = 0
set MODE = VIDEO
set FRAMES_PER_SECOND = 60
LOOP
IF new field has now arrived on TV card
//
// Automatic detection code starts here
// Compares a pair of even fields or a pair of odd fields.
//
GRAB new current field (add to queue of fields)
COMPARE two fields: current field and two fields ago.
IF fields DO NOT match THEN
IF MISMATCH_COUNT does not exceed MAX_MISMATCH
increment MISMATCH_COUNT by 1
//
// MISMATCH_COUNT is the number of consecutive pairs
// of fields that do not match.
ELSE
set MODE = VIDEO
set MOVIE_VERIFY_CYCLE = 0
set MOVIE_FIELD_CYCLE = 0
//
// There has been no duplicate fields lately.
// It's probably video source.
//
// MAX_MISMATCH should be a reasonably high value so
// that we do not have an oversensitive hair-trigger
// in switching to video source everytime there is
// video noise or a single spurious field added/dropped
// during a movie causing mis-synchronization problems.
ENDIF
ELSE IF fields match THEN
// It's either a stationary image OR a duplicate field in a movie
IF MISMATCH_COUNT is 4
//
// 3:2 pulldown is a cycle of 5 fields where there is only
// one duplicate field pair, and 4 mismatching pairs.
// We need to continue detection for at least 2 cycles
// to be very certain that it is actually 3:2 pulldown
// This would mean a latency of 10 fields.
//
IF MOVIE_VERIFY_CYCLE equals or exceeds MAX_VERIFY_CYCLES
//
// This executes regardless whether we've just entered or
// if we're *already* in 3:2 pulldown. Either way, we are
// currently now (re)synchronized to 3:2 pulldown and that
// we've now detected the duplicate field.
//
set MODE = MOVIE
set MOVIE_FIELD_CYCLE = 0
ELSE
increment MOVIE_VERIFY_CYCLE by 1
ENDIF
ELSE MISMATCH_COUNT not equal to 4
//
// If execution point hits here, it is probably just
// stationary video. That would produce lots of duplicate
// field pairs. We can't determine whether it's video or
// movie source. Therefore, we want to keep doing the
// current algorithm from a prior detection. Therefore,
// don't modify any variables EXCEPT the MISMATCH_COUNT
// variable which will be reset to 0 below.
// Also, occasionally we'll hit here during synchronization
// problems during a movie, such as a spurious field added
// or dropped. Reset MISMATCH_COUNT to 0 below and let
// detection cycle happen again in order to re-synchronize.
// Keep the MODE variable the way it currently is.
ENDIF
set MISMATCH_COUNT = 0
ENDIF
// Now, process the images depending on present mode
IF MODE is MOVIE
// You could replace below with movie source deinterlacer plugin
//
set FRAMES_PER_SECOND = 24
BEGIN movie-source deinterlacer
IF MOVIE_FIELD_CYCLE equals 2 or MOVIE_FIELD_CYCLE equals 4
Weave current field with previous field
Add resulting field to OUTPUT queue
ELSE
// Do nothing here.
// Do not add frame to OUTPUT queue.
// During MOVIE_FIELD_CYCLE 0 and 3, the current and
// previous fields come from different movie frames.
// During MOVIE_FIELD_CYCLE 1, the current and previous
// fields identical movie frame as MOVIE_FIELD_CYCLE 2.
// Therefore, we skip these cycle values.
ENDIF
// Looping counter that goes from 0, 1, 2, 3, 4 and back to 0
increment MOVIE_FIELD_CYCLE by 1
IF MOVIE_FIELD_CYCLE equals 5 THEN
set MOVIE_FIELD_CYCLE = 0
ENDIF
END
ELSEIF MODE is VIDEO
// You could replace below with video source deinterlacer plugin
//
set FRAMES_PER_SECOND = 60
BEGIN video-source deinterlacer
Apply video source deinterlace algorithm
Add resulting field to OUTPUT queue
END
ENDIF
ENDIF
// You can put video output code here for the OUTPUT queue of frames.
// You can optionally synchronize to the output refresh this way.
ENDLOOP
.
EXAMPLE SEQUENCE OF FRAMES FROM 3:2 PULLDOWN
Field 00: Movie frame 1
Field 01: Movie frame 1
Field 02: Movie frame 1
Field 03: Movie frame 2
Field 04: Movie frame 2
Field 05: Movie frame 3
Field 06: Movie frame 3
Field 07: Movie frame 3
Field 08: Movie frame 4
Field 09: Movie frame 4
Field 10: Movie frame 5
Field 11: Movie frame 5
Field 12: Movie frame 5
Field 13: Movie frame 6
Field 14: Movie frame 6
Field 02 and 00 is MATCH --- MOVIE_FIELD_CYCLE = 0
Field 03 and 01 is mismatch - MOVIE_FIELD_CYCLE = 1
Field 04 and 02 is mismatch - MOVIE_FIELD_CYCLE = 2
Field 05 and 03 is mismatch - MOVIE_FIELD_CYCLE = 3
Field 06 and 04 is mismatch - MOVIE_FIELD_CYCLE = 4
Field 07 and 05 is MATCH --- MOVIE_FIELD_CYCLE = 0
Field 08 and 06 is mismatch - MOVIE_FIELD_CYCLE = 1
Field 09 and 07 is mismatch - MOVIE_FIELD_CYCLE = 2
Field 10 and 08 is mismatch - MOVIE_FIELD_CYCLE = 3
Field 11 and 09 is mismatch - MOVIE_FIELD_CYCLE = 4
Field 12 and 10 is MATCH --- MOVIE_FIELD_CYCLE = 0
Field 13 and 11 is mismatch - MOVIE_FIELD_CYCLE = 1
Field 14 and 12 is mismatch - MOVIE_FIELD_CYCLE = 2
.
OPTIONAL: BETTER SYNCHRONIZATION TO MONITOR REFRESH
It is desirable to make the output refresh independent of the
input refresh for better synchronization. During 3/2 pulldown
material, it is very desirable to synchronize to the monitor
refresh instead of the input TV signal refresh. During watching
in normal TV viewers, 3/2 pulldown is doubled to 6/4 pulldown
when the refresh rate is doubled to 120 Hz. It would be very nice
to have something closer to 5/5 pulldown when watching movie
material instead of 6/4 pulldown.
In the pseudocode above in the last section,
There are 24 frames per second in OUTPUT queue in MOVIE mode
There are 60 frames per second in OUTPUT queue in VIDEO mode
You can synchronize the OUTPUT queue of fields to the monitor
refresh, based on the value in the FRAMES_PER_SECOND variable. By
keeping a small output buffer of frames (of about 2 or so frames)
you can keep the pipeline of output frames flowing smoothly and
synchronized much better to the computer monitor.
Also, since the computer clock is accurate, this can help
"stabilize" unstable NTSC refresh such as those from a VCR which
can sometimes have slightly-varying refresh rates (fluctuating
randomly as low as 59Hz all the way up to 61Hz although usually
by smaller increments). This can interact badly with a consistent
computer refresh. Regular TV sets have a very slight amount of
"give" that allows it to compensate for very minor fluctuations
in refresh rate, but computer monitors won't compensate.
It can also smooth out jerkiness problems caused by
fluctuating CPU usage on a frame-by-frame basis, since
deinterlacing one pair of frames might take longer than
deinterlacing a different pair of frames (using certain video
source deinterlacing algorithms).
There is also the problem of compensating for drift when the
output framerate is decoupled from the refresh of the input
signal. There are two good possible ways of getting around this
drift problem:
(1) AVOID displaying the output frame if there is less than 2
frames in the OUTPUT queue, and ACCELERATE displaying of the next
frame if the number of frames in the OUTPUT queue exceeds a
predetermined value such as 4. That way, you always keep 2, 3 or
4 frames buffered at all times for maximum fluidity. (You could
experiment, and set the minimum to 1 and maximum to 3, to find
out which produces the best fluidity at the minimum possible
buffering. Too much buffering will cause audio to be noticeably
lagged behind the video)
(2) Synchronize to a clock that's generated from the TV tuner
card, instead of using the computer's clock. For example,
increment an integer clock counter by 1 everytime a field arrives
from the TV card. This clock would be incremented about 59.94
times per second for most NTSC signals. Keep an eye on the
computer's clock everytime you increment the integer clock by 60
- to calculate the exact amount of time it takes for 60 fields to
hit the TV tuner card. Do this continously every second. Divide
this value by 24 to compute the amount of time there should be
between frames, during MOVIE mode, during the next second. (If
you prefer, you could use 5 second cycles instead of 1 second
cycles, or keep a "rolling average" every field based on the
previous X number of fields).
.
APPENDIX: PSEUDOCODE WITHOUT COMMENTS
For determining the amount of "code bulk", this is how small
the pseudocode is, when I remove my comments from the above
pseudocode. Look at how small the pseudocode is - since you
already have the routines written in DScaler, look at how simple
it is to implement automatic 3:2 pulldown deinterlacing!
set MAX_MISMATCH = 12
set MAX_VERIFY_CYCLES = 2
set MISMATCH_COUNT = 0
set MOVIE_FIELD_CYCLE = 0
set MOVIE_VERIFY_CYCLE = 0
set MODE = VIDEO
set FRAMES_PER_SECOND = 60
LOOP
á IF new field has now arrived on TV card
ááá GRAB new current field (add to queue of fields)
ááá COMPARE two fields: current field and two fields ago.
ááá IF fields DO NOT match THEN
ááááááá IF MISMATCH_COUNT does not exceed MAX_MISMATCH
ááááááááááá increment MISMATCH_COUNT by 1
ááááááá ELSE
ááááááááááá set MODE = VIDEO
ááááááááááá set MOVIE_VERIFY_CYCLE = 0
ááááááááááá set MOVIE_FIELD_CYCLE = 0
ááááááá ENDIF
ááá ELSE IF fields match THEN
ááááááá IF MISMATCH_COUNT is 4
ááááááááááá IF MOVIE_VERIFY_CYCLE equals or exceeds MAX_VERIFY_CYCLES
ááááááááááááááá set MODE = MOVIE
ááááááááááááááá set MOVIE_FIELD_CYCLE = 0
ááááááááááá ELSE
ááááááááááááááá increment MOVIE_VERIFY_CYCLE by 1
ááááááááááá ENDIF
ááááááá ELSE MISMATCH_COUNT not equal to 4
ááááááá ENDIF
ááááááá set MISMATCH_COUNT = 0
ááá ENDIF
ááá
ááá IF MODE is MOVIE
ááááááá set FRAMES_PER_SECOND = 24
ááááááá BEGIN movie-source deinterlacer
ááááááááááá IF MOVIE_FIELD_CYCLE equals 2 or MOVIE_FIELD_CYCLE equals 4
áááááááááááááááá Weave current field with previous field
áááááááááááááááá Add resulting field to OUTPUT queue
ááááááááááá ENDIF
ááááááááááá increment MOVIE_FIELD_CYCLE by 1
ááááááááááá IF MOVIE_FIELD_CYCLE equals 5 THEN
ááááááááááááááá set MOVIE_FIELD_CYCLE = 0
ááááááááááá ENDIF
ááááááá END
ááá ELSEIF MODE is VIDEO
ááááááá set FRAMES_PER_SECOND = 60
ááááááá BEGIN video-source deinterlacer
ááááááááááá Apply video source deinterlace algorithm
ááááááááááá Add resulting field to OUTPUT queue
ááááááá END
ááá ENDIF
á ENDIF
ENDLOOP
(c) Mark Rejhon
2000
|